//
// Created by DELL on 2022/4/20.
//

#include "Model.h"

#include <utility>
#include "Device.h"

BEGIN_NS_VULKAN

Model::Model(DeviceRef device)
    : device(std::move(device))
{

}

Model::~Model()
{
    vkDestroyBuffer(device->getDevice(), vertexBuffer, nullptr);
    vkFreeMemory(device->getDevice(), vertexBufferMemory, nullptr);
}

void Model::bind(VkCommandBuffer commandBuffer)
{
    VkBuffer buffers[] = { vertexBuffer };
    VkDeviceSize offsets[] = { 0 };
    vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets);
}

void Model::draw(VkCommandBuffer commandBuffer)
{
    vkCmdDraw(commandBuffer, vertexCount, 1, 0, 0);
}

void Model::createVertexBuffers(const void* data, int size)
{
    device->createBuffer(size,
        VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
        vertexBuffer,
        vertexBufferMemory);

    void* vertData{};
    vkMapMemory(device->getDevice(), vertexBufferMemory, 0, size, 0, &vertData);
    memcpy(vertData, data, static_cast<size_t>(size));
    vkUnmapMemory(device->getDevice(), vertexBufferMemory);
}

///-------------Model2D------------------------///
std::vector<VkVertexInputBindingDescription> Model2D::Vertex::getBindingDescriptions()
{
    std::vector<VkVertexInputBindingDescription> bindingDescriptions(1);
    bindingDescriptions[0].binding = 0;
    bindingDescriptions[0].stride = sizeof(Vertex);
    bindingDescriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
    return bindingDescriptions;
}

std::vector<VkVertexInputAttributeDescription> Model2D::Vertex::getAttributeDescriptions()
{
    std::vector<VkVertexInputAttributeDescription> attributeDescriptions(2);
    attributeDescriptions[0].binding = 0;
    attributeDescriptions[0].location = 0;
    attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
    attributeDescriptions[0].offset = 0;

    attributeDescriptions[1].binding = 0;
    attributeDescriptions[1].location = 1;
    attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
    attributeDescriptions[1].offset = offsetof(Vertex, color);
    return attributeDescriptions;
}

Model2D::Model2D(DeviceRef& device) : Model(device)
{

}

Model2DRef Model2D::create(DeviceRef& device, const std::vector<Vertex>& vertices)
{
    auto model = new Model2D(device);
    model->vertexCount = static_cast<uint32_t>(vertices.size());
    model->createVertexBuffers(vertices.data(), vertices.size() * sizeof(Vertex));

    return Model2DRef(model);
}

///-------------Model2DTex------------------------///
std::vector<VkVertexInputBindingDescription> Model2DTex::Vertex::getBindingDescriptions()
{
    std::vector<VkVertexInputBindingDescription> bindingDescriptions(1);
    bindingDescriptions[0].binding = 0;
    bindingDescriptions[0].stride = sizeof(Vertex);
    bindingDescriptions[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
    return bindingDescriptions;
}

std::vector<VkVertexInputAttributeDescription> Model2DTex::Vertex::getAttributeDescriptions()
{
    std::vector<VkVertexInputAttributeDescription> attributeDescriptions(3);
    attributeDescriptions[0].binding = 0;
    attributeDescriptions[0].location = 0;
    attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
    attributeDescriptions[0].offset = 0;

    attributeDescriptions[1].binding = 0;
    attributeDescriptions[1].location = 1;
    attributeDescriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
    attributeDescriptions[1].offset = offsetof(Vertex, texCoord);

    attributeDescriptions[2].binding = 0;
    attributeDescriptions[2].location = 2;
    attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT;
    attributeDescriptions[2].offset = offsetof(Vertex, color);
    return attributeDescriptions;
}

Model2DTex::Model2DTex(DeviceRef& device) : Model(device)
{
}

Model2DTexRef Model2DTex::create(DeviceRef& device, const std::vector<Vertex>& vertices)
{
    auto model = new Model2DTex(device);
    model->vertexCount = static_cast<uint32_t>(vertices.size());
    model->createVertexBuffers(vertices.data(), vertices.size() * sizeof(Vertex));

    return Model2DTexRef(model);
}

///-------------Model3D------------------------///


END_NS_VULKAN
